const BluetoothPrinter = {
    errorCode: {
        '-1': '蓝牙已连接',
        0: '正常',
        10000: '未初始化蓝牙适配器',
        10001: '当前蓝牙适配器不可用',
        10002: '没有找到指定设备',
        10003: '连接失败',
        10004: '没有找到指定服务',
        10005: '没有找到指定特征值',
        10006: '当前连接已断开',
        10007: '当前特征值不支持此操作',
        10008: '其余所有系统上报的异常',
        10009: 'Android 系统特有，系统版本低于 4.3 不支持 BLE',
        10010: '已连接',
        10011: '配对设备需要配对码',
        10012: '连接超时',
        10013: '为空或者是格式不正确',
    },
    device:{
        deviceId: "",
        writeCharaterId: "",
        writeServiceId: "",
        notifyCharaterId: "",
        notifyServiceId: "",
        readCharaterId: "",
        readServiceId: "",
        printDelayTime: 20,
    },
    /**
     * 通过错误码获取错误描述
     * @param code 错误码
     * @param message 未匹配到错误码时的默认错误描述
     * @returns {*|string}
     */
    getMessage(code, message = "未知错误") {
        return this.errorCode[code] ? this.errorCode[code] : message;
    },
    setDevice(device){
        this.device = device;
        return this;
    },
    openSettingPage() {
        // 安卓设备提示开启蓝牙
        // #ifdef APP-PLUS
        let main = plus.android.runtimeMainActivity();
        let Intent = plus.android.importClass('android.content.Intent');
        let Settings = plus.android.importClass('android.provider.Settings');
        main.startActivity(new Intent(Settings.ACTION_BLUETOOTH_SETTINGS));
        // #endif
    },
    /**
     * 蓝牙是否启用
     */
    isEnabled() {
        return this.getAdapterState().then(res => {
            return res.errCode !== 10000
        });
    },
    isAvailable() {
        return this.getAdapterState().then(res => res.available || false);
    },
    isDiscovering() {
        return this.getAdapterState().then(res => res.discovering || false);
    },
    getAdapterState() {
        return new Promise((resolve, reject) => {
            uni.getBluetoothAdapterState({
                success: (res) => {
                    console.log(res);
                    resolve(res);
                },
                fail: (res) => {
                    console.log(res);
                    reject(res);
                }
            })
        });
    },
    async openAdapter() {
        return new Promise((reslove, reject) => {
            uni.openBluetoothAdapter({
                success: (res) => {
                    console.log("openAdapter-success,res:", res);
                    uni.onBluetoothAdapterStateChange((res) => {
                        console.log('adapterState changed, now is', res)
                        //{"discovering":false,"available":false}
                        /*if(res?.available){
                            this.openAdapter();
                        } else {
                            this.close();
                        }*/
                    })
                    reslove(res);
                }, fail: (res) => {
                    console.log("openAdapter-fail,res:", res);
                    if (res.errCode === 10001 || res.code === 10001) {
                        //如果没打开蓝牙提示用户打开蓝牙
                        // #ifdef APP-PLUS
                        let platform = uni.getSystemInfoSync().platform;
                        if (platform === 'android') {
                            let BluetoothAdapter = plus.android.importClass("android.bluetooth.BluetoothAdapter");
                            let BAdapter = BluetoothAdapter.getDefaultAdapter();
                            if (!BAdapter.isEnabled()) {
                                BAdapter.enable();
                            } else {
                                this.openAdapter();
                            }
                        }
                        // #endif
                    }
                    reject(res);
                }
            })
        })
    },
    close() {
        return new Promise((reslove, reject) => {
            uni.closeBluetoothAdapter({
                success() {
                    reslove();
                }, fail(res) {
                    reject(res);
                }
            });
        });
    },
    closeConnect(deviceId) {
        return new Promise((resolve, reject) => {
            uni.closeBLEConnection({
                deviceId: deviceId, success() {
                    resolve();
                }, fail(res) {
                    if (res.errCode === 10004) {
                        resolve();
                    } else {
                        console.log(res);
                        reject(res);
                    }
                }
            })
        })
    },
    async startSearch(onDeviceFind) {
        return new Promise((resolve, reject) => {
            uni.startBluetoothDevicesDiscovery({
                success: (res) => {
                    let i = 0;
                    let intervalId = setInterval(() => {
                        this.getDevices().then(devices => {
                            if( typeof onDeviceFind == 'function'){
                                onDeviceFind(devices);
                            }
                            if (i++ >= 5) {
                                clearInterval(intervalId);
                                this.stopSearch();
                                resolve(devices);
                            }
                        });
                    }, 1000);
                }, fail: (res) => {
                    reject(res);
                }
            })
        });
    },
    getDevices() {
        console.log('getDevices====>');
        return new Promise((resolve, reject) => {
            uni.getBluetoothDevices({
                success: (res) => {
                    console.log("getDevices-success:res", res);
                    let devices = res.devices.filter(e => e.name && e.name !== '未知设备');

                    resolve(devices);
                }, fail(res) {
                    console.log("getDevices-fail:res", res);
                    reject(res);
                },complete(res){
                    console.log('getDevices====>complete:res',res);
                }
            })
        });
    },
    stopSearch() {
        return new Promise((resolve, reject) => {
            uni.stopBluetoothDevicesDiscovery({
                success: (res) => {
                    resolve(res);
                }, fail: (res) => {
                    reject(res);
                }
            })
        });
    },
    connect(deviceId) {
        console.log(`connect deviceId0`, deviceId)
        return new Promise((reslove, rejcet) => {
            console.log(`connect deviceId1`, deviceId)
            uni.createBLEConnection({
                deviceId,
                success: (res) => {
                    console.log(`device ${deviceId} createBLEConnection, success: `, deviceId)
                    uni.onBLEConnectionStateChange(function (res1) {
                        // 该方法回调中可以用于处理连接意外断开等异常情况
                        console.log(`device ${res1.deviceId} state has changed, connected: `, res1)
                    })
                    reslove(res);
                },
                fail: (res) => {
                    rejcet(res)
                }
            })
        });
    },
    getServices(deviceId) {
        let times = 0;
        return new Promise((reslove, reject) => {
            tryGetService(reslove, reject);
        })

        function tryGetService(reslove, reject) {
            uni.getBLEDeviceServices({
                deviceId,
                success: (res) => {
                    // 处理在刚连接到蓝牙时,services获取为空的问题
                    if (res.services.length > 0) {
                        reslove(res.services);
                    } else if (times++ < 10) {
                        setTimeout(() => {
                            tryGetService(reslove, reject);
                        }, 200 * times);
                    } else {
                        console.log("services get error");
                        reject(res);
                    }
                }, fail: (res) => {
                    console.log(res);
                    reject(res);
                },
            })
        }
    },
    // 打印机初始化
    init(deviceId) {
        let retryTimes = 0;
        let _this = this;
        return new Promise((resolve, reject) => {
            execInit(resolve, reject)
        })

        function execInit(resolve, reject) {
            console.log('连接蓝牙,deviceId', deviceId);
            _this.connect(deviceId).then((res) => {
                console.log('蓝牙连接成功', res);
                _this.getServices(deviceId).then((res) => {
                    console.log('获取服务成功', res);
                    let services = res;
                    _this.getCharacteristics(deviceId, services).then((res) => {
                        console.log('获取特征码成功', res);
                        /*
                        res的值果示例如下:
                        {
                            "notifyCharaterId": "00002A19-0000-1000-8000-00805F9B34FB",
                            "notifyServiceId": "0000180F-0000-1000-8000-00805F9B34FB",
                            "writeCharaterId": "0000FFF1-0000-1000-8000-00805F9B34FB",
                            "writeServiceId": "0000FF10-0000-1000-8000-00805F9B34FB",
                            "readServiceId": "49535343-FE7D-4AE5-8FA9-9FAFD205E455",
                            "readCharaterId": "0000FF15-0000-1000-8000-00805F9B34FB"
                        }*/

                        /*
                        // 如果notifyCharaterId有值，则需要监听设备特征码的变更通知
                        if(characteristics.notifyCharaterId){
                            this.notify(deviceId, res.notifyServiceId, res.notifyCharaterId).finally((res) => {
                            })
                        }
                        */

                        // plus.storage.setItem('writeServiceId', res.writeServiceId);
                        // plus.storage.setItem('writeCharacteristicId', res.writeCharaterId);
                        resolve(res);
                    }).catch((res) => {
                        console.log('获取特征码失败', res);
                        reject(res);
                    })
                }).catch((res) => {
                    console.log('获取服务失败', res);
                    reject(res);
                })
            }).catch((res) => {
                console.log('连接蓝牙失败', res);
                // {"errMsg":"createBLEConnection:fail already connect","code":-1}
                if (res?.code === 10000) {
                    _openAdapter().then((res) => {
                        console.log('打开蓝牙适配器成功', res);
                        if (retryTimes < 3) {
                            execInit(resolve, reject);
                        } else {
                            console.log('重试超过3次，返回失败', res);
                            reject(res);
                        }
                    }).catch((res) => {
                        console.log('打开蓝牙适配器失败', res);
                        reject(_this.parseResult(res));
                    })
                } else if(res?.code === 10002){  //{"errMsg":"createBLEConnection:fail no device,https://ask.dcloud.net.cn/article/282","code":10002}
                    if (retryTimes < 3) {
                        setTimeout(function (){
                            execInit(resolve, reject);
                        }, 500)
                    } else {
                        console.log('重试超过3次，返回失败', res);
                        reject(res);
                    }
                } else if  (res?.code === 10012 || res?.errMsg === 'createBLEConnection:fail already connect') {
                    console.log('连接蓝牙失败', res);
                    //需要断开连接重新连
                    /*_this.closeConnect(deviceId).finally(() => {
                        reject(res);
                    });*/
                    _this.getServices(deviceId).then((res) => {
                        console.log('获取服务成功', res);
                        let services = res;
                        _this.getCharacteristics(deviceId, services).then((res) => {
                            console.log('获取特征码成功', res);
                            /*
                            res的值果示例如下:
                            {
                                "notifyCharaterId": "00002A19-0000-1000-8000-00805F9B34FB",
                                "notifyServiceId": "0000180F-0000-1000-8000-00805F9B34FB",
                                "writeCharaterId": "0000FFF1-0000-1000-8000-00805F9B34FB",
                                "writeServiceId": "0000FF10-0000-1000-8000-00805F9B34FB",
                                "readServiceId": "49535343-FE7D-4AE5-8FA9-9FAFD205E455",
                                "readCharaterId": "0000FF15-0000-1000-8000-00805F9B34FB"
                            }*/
                            let characteristics = res;

                            /*
                            // 如果notifyCharaterId有值，则需要监听设备特征码的变更通知
                            if(characteristics.notifyCharaterId){
                                this.notify(deviceId, characteristics.notifyServiceId, characteristics.notifyCharaterId).finally((res) => {
                                })
                            }
                            */

                            resolve(res);
                        }).catch((res) => {
                            console.log('获取特征码失败', res);
                            reject(res);
                        })
                    }).catch((res) => {
                        console.log('获取服务失败', res);
                        reject(res);
                    })
                } else {
                    console.log('连接蓝牙失败', res);
                    reject(res);
                }
            })
        }

        function _openAdapter(){
            return _this.openAdapter();
        }
    },
    /**
     * 监听 notify 特征值的变化
     * @param deviceId
     * @param serviceId
     * @param characteristicId
     * @returns {Promise<unknown>}
     */
    notify(deviceId, serviceId, characteristicId) {
        return new Promise((resolve, reject) => {
            uni.notifyBLECharacteristicValueChange({
                deviceId,
                serviceId,
                characteristicId,
                state: true,
                success: (res) => {
                    console.log(res);
                    console.log('printer init finish');
                    uni.onBLECharacteristicValueChange(function (r) {
                        console.log(`characteristic ${r.characteristicId} has changed, now is`, r)
                    })
                    resolve(res);
                },
                fail: function (e) {
                    console.log(e)
                    reject(e);
                }
            });
        })
    },
    /**
     * 返回蓝牙的读写特征值
     * @param {Object} deviceId
     * @param {Object} services
     */
    async getCharacteristics(deviceId, services) {
        let result = {
            notifyCharaterId: '',
            notifyServiceId: '',
            writeCharaterId: '',
            writeServiceId: '',
            readServiceId: '',
            readCharaterId: ''
        }
        let serviceIndex = 0;
        return new Promise((reslove, reject) => {
            getBLEDeviceCharacteristics(reslove, reject);
        });

        function getBLEDeviceCharacteristics(reslove, reject) {
            console.log("getBLEDeviceCharacteristics:serviceIndex", serviceIndex);
            console.log("getBLEDeviceCharacteristics:services", services);
            uni.getBLEDeviceCharacteristics({
                deviceId,
                serviceId: services[serviceIndex].uuid,
                success: (res) => {
                    for (var i = 0; i < res.characteristics.length; ++i) {
                        var properties = res.characteristics[i].properties
                        console.log(properties);
                        var item = res.characteristics[i].uuid
                        if (!result.notifyCharaterId && properties.notify) {
                            result.notifyCharaterId = item
                            result.notifyServiceId = services[serviceIndex].uuid
                        } else if (!result.writeCharaterId && properties.write) {
                            result.writeCharaterId = item
                            result.writeServiceId = services[serviceIndex].uuid
                        } else if (!result.readCharaterId && properties.read) {
                            result.readCharaterId = item
                            result.readServiceId = services[serviceIndex].uuid
                        }
                    }
                    //if (result.readCharaterId && result.writeCharaterId && result.notifyCharaterId) {
                    if ( result.writeCharaterId ) {
                        reslove(result);
                        return;
                    }
                    if (serviceIndex === services.length) {
                        reject(result);
                        return;
                    }
                    serviceIndex++
                    getBLEDeviceCharacteristics(reslove, reject);
                },
                fail: function (res) {
                    reject(res);
                }
            })
        }
    },
    // 查询打印机状态
    getStatus() {
        console.log("getStatus====> enter");
        return new Promise((resolve, reject) => {
            let deviceId = this.device.deviceId;
            let serviceId = this.device.writeServiceId;
            let characteristicId = this.device.writeCharaterId;
            console.log(`getStatus====> deviceId: ${deviceId}, serviceId:${serviceId}, characteristicId:${characteristicId}`);

            if (!deviceId) {
                return reject({"code": 3000, "message": "蓝牙打印机未连接"});
            }
            if (!serviceId) {
                return reject({"code": 3001, "message": "未获取蓝牙服务"});
            }
            if (!characteristicId) {
                return reject({"code": 3002, "message": "未获取蓝牙特征码"});
            }

            /*
            n = 1：传送打印机状态
            n = 2：传送脱机状态
            n = 3：传送错误状态
            n = 4：传送纸传感器状态
            */
            let n = 1;
            let buf = new ArrayBuffer(3)
            let dateView = new DataView(buf)
            dateView.setUint8(0, 16)
            dateView.setUint8(1, 4)
            dateView.setUint8(2, n)
            uni.writeBLECharacteristicValue({
                deviceId,
                serviceId,
                characteristicId,
                value: buf,
                success: (res) => {
                    console.log("uni.writeBLECharacteristicValue-success,res:", res);
                    resolve(this.parseResult(res));
                },
                fail: (res) => {
                    console.log("uni.writeBLECharacteristicValue-fail,res:", res);
                    this.getAdapterState()
                    reject(this.parseResult(res));
                },
                complete: (res) => {
                    console.log("uni.writeBLECharacteristicValue-complete,res:", res);
                    buf = null
                    dateView = null;
                }
            })
        })
    },
    checkAndPrint(buff) {
        let _this = this;
        return new Promise((resolve, reject) => {
            this.getStatus().then((res) => {
                this.sendData(buff).then(res1  => {
                    resolve({"code": 0, "message": "已打印"});
                });
            }).catch((res) => {
                console.log("checkAndPrint====>catch:res1", res);
                if (res?.code === 10000 || res?.code === 3001 || res?.code === 3002) {
                    retryPrint(resolve, reject);
                } else if (res?.code === 10004) {
                    this.getAdapterState().then((res) => {
                        console.log("checkAndPrint====>this.getAdapterState:res", res);
                        retryPrint(resolve, reject);
                    }).catch((res) => {
                        reject(res);
                    });
                } else if (res?.code === 10002){  // 苹果设备需要先搜索蓝牙之后才能打印
                    this.startSearch(function (res){
                        console.log("startSearch,callback,res",res);
                    }).then((res) => {
                        console.log("checkAndPrint====>this.startSearch:res", res);
                        retryPrint(resolve, reject);
                    }).catch((res) => {
                        reject(res);
                    });
                }else{
                    reject(res);
                }
            })
        });
        function retryPrint(resolve, reject){
            let deviceId = _this.device.deviceId;
            console.log("checkAndPrint====>retryPrint:deviceId", deviceId);
            _this.init(deviceId).then((res) => {
                console.log("checkAndPrint====>_this.init,then:res", res);
                _this.sendData(buff).then(res => {
                    resolve({"code": 0, "message": "已打印"});
                }).catch((res) => {
                    resolve({"code": 1, "message": "发送打印数据异常"});
                })
            }).catch((res) => {
                console.log("checkAndPrint====>_this.init,catch:res", res);
                reject(_this.parseResult(res)); //{"errMsg":"createBLEConnection:fail already connect","errCode":-1,"code":-1}
            })
        }
    },
    // 发送数据至打印机
    sendData(buff) {
        let currentTime = 1, oneTimeData = 20;
        let lastData = parseInt(buff.length % oneTimeData);
        let loop = parseInt(buff.length / oneTimeData);
        let loopTime = lastData > 0 ? loop + 1 : loop;
        let deviceId = this.device.deviceId;
        let serviceId = this.device.writeServiceId;
        let characteristicId = this.device.writeCharaterId;
        let printDelayTime = this.device.printDelayTime;

        let delay = false;
        // 当前重试次数
        let retryTimes = 0;
        // 最大重试次数
        let maxRetryTimes = 5;
        let lastLoop = false;

        return new Promise((resolve, reject) => {
            send(resolve, reject);
        })

        function send(resolve, reject) {
            let buf, dataView
            if (currentTime < loopTime) {
                buf = new ArrayBuffer(oneTimeData)
                dataView = new DataView(buf)
                for (let i = 0; i < oneTimeData; i++) {
                    dataView.setUint8(i, buff[(currentTime - 1) * oneTimeData + i])
                }
            } else {
                let lastLength = lastData > 0 ? lastData : oneTimeData;
                buf = new ArrayBuffer(lastLength)
                dataView = new DataView(buf)
                for (let i = 0; i < lastLength; i++) {
                    dataView.setUint8(i, buff[(currentTime - 1) * oneTimeData + i])
                }
                if (!lastLoop) {
                    lastLoop = true;
                }
            }
            console.log("第" + currentTime + "次发送数据大小为：" + buf.byteLength);
            uni.writeBLECharacteristicValue({
                deviceId: deviceId,
                serviceId: serviceId,
                characteristicId: characteristicId,
                value: buf,
                success: (res) => {
                    currentTime++
                    retryTimes = 0;
                },
                fail: (e) => {
                    retryTimes++;
                    delay = true;
                    console.error(e)
                },
                complete: (res) => {
                    if (currentTime <= loopTime && retryTimes < maxRetryTimes) {
                        if (delay || printDelayTime > 0) {
                            delay = false;
                            setTimeout(() => {
                                send(resolve, reject);
                            }, printDelayTime > 0 ? printDelayTime : 100);
                        } else {
                            send(resolve, reject);
                        }
                    }
                    if (lastLoop) {
                        console.log("resolve,res", resolve, res);
                        resolve();
                    }
                }
            })
        }
    },
    /**
     * 转换返回结果
     * 转换前：{"errMsg":"writeBLECharacteristicValue:fail not init","errCode":10000,"code":10000}
     * 转换后：{"code":10000, "message":"未初始化蓝牙适配器"}
     * @param res 转换前的数据
     * @return
     */
    parseResult(res) {
        let code = res?.code ? res?.code : res
        return {
            code: code,
            message: this.getMessage(code)
        }
    }
}

export default BluetoothPrinter;